using System;
using System.BusinessObjects.Data;
using System.Security.Cryptography;
using System.Text;
using Blazing.BusinessObjects.Locations;

namespace Blazing.BusinessObjects.Cms
{
    /// <summary>
    /// Represents geocoded information for a location
    /// </summary>
    [Serializable]
    public class Geocode : DataObject<Geocode>
    {
        public virtual int ID
        {
            get { return GetValue<int>("ID"); }
            set
            {
                BeginEdit();
                SetValue("ID", value);
            }
        }

        public virtual double Latitude
        {
            get { return GetValue<double>("Latitude", 0D); }
            set
            {
                BeginEdit();
                SetValue("Latitude", value);
            }
        }

        public virtual double Longatude
        {
            get { return GetValue<double>("Longatude", 0D); }
            set
            {
                BeginEdit();
                SetValue("Longatude", value);
            }
        }

        public virtual int Accuracy
        {
            get { return GetValue<int>("Accuracy"); }
            set
            {
                BeginEdit();
                SetValue("Accuracy", value);
            }
        }

        public virtual string HashLookup
        {
            get { return GetValue<string>("HashLookup"); }
            set
            {
                BeginEdit();
                SetValue("HashLookup", value);
            }
        }

        public DateTime RowStamp
        {
            get { return GetValue<DateTime>("RowStamp"); }
            set
            {
                SetValue("RowStamp", value);
            }
        }

        public virtual double DistanceTo(double Lat2, double Long2)
        {
            /*
                The Haversine formula according to Dr. Math.
                http://mathforum.org/library/drmath/view/51879.html
                
                dlon = lon2 - lon1
                dlat = lat2 - lat1
                a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
                c = 2 * atan2(sqrt(a), sqrt(1-a)) 
                d = R * c
                
                Where
                    * dlon is the change in longitude
                    * dlat is the change in latitude
                    * c is the great circle distance in Radians.
                    * R is the radius of a spherical Earth.
                    * The locations of the two points in 
                        spherical coordinates (longitude and 
                        latitude) are lon1,lat1 and lon2, lat2.
            */

            if (IsNull("Latitude") || IsNull("Longatude"))
                return 0;

            double Lat1 = Convert.ToDouble(Latitude);
            double Long1 = Convert.ToDouble(Longatude);

            double dDistance;
            double dLat1InRad = Lat1 * (Math.PI / 180.0);
            double dLong1InRad = Long1 * (Math.PI / 180.0);
            double dLat2InRad = Lat2 * (Math.PI / 180.0);
            double dLong2InRad = Long2 * (Math.PI / 180.0);

            double dLongitude = dLong2InRad - dLong1InRad;
            double dLatitude = dLat2InRad - dLat1InRad;

            // Intermediate result a.
            double a = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) +
                       Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) *
                       Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);

            // Intermediate result c (great circle distance in Radians).
            double c = 2.0 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a));

            // Distance.
            // const Double kEarthRadiusMiles = 3956.0;
            const Double kEarthRadiusKms = 6376.5;
            dDistance = kEarthRadiusKms * c;

            return dDistance;
        }

        public static string CalculateMD5Hash(Location location)
        {
            string hash = System.BusinessObjects.Providers.CacheProvider.CreateCacheString("", location.Namelong, location.ParentLocation != null ? location.ParentLocation.Namelong : "");
            return CalculateMD5Hash(hash);
        }

        public static string CalculateMD5Hash(Address address)
        {
            string hash = System.BusinessObjects.Providers.CacheProvider.CreateCacheString("", address.Address1, address.Address2, address.Address3, address.Suburb, address.Postcode, address.State != null ? address.State.Name: null, address.Country != null ? address.Country.Name : null);
            return CalculateMD5Hash(hash);
        }

        public static string CalculateMD5Hash(string input)
        {
            // calculate MD5 hash from input
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            // convert byte array to hex string
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }
    }
}
